﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Linq;
using System.IO;

namespace AppEntity
{
    
    /// <summary>
    /// C# XML 基础类库扩展
    /// </summary>

    public static class XmlExtension
    {
        /// <summary>
        /// 遍历 XmlNodeList 集合，每个元素都传入委托 F。
        /// </summary>
        /// <param name="NodeList"></param>
        /// <param name="F"></param>
        public static void Each(this XmlNodeList NodeList, Action<XmlNode> F)
        {
            foreach (XmlNode Node in NodeList)
            {
                F(Node);
            }
        }
        /// <summary>
        /// 遍历 XmlNodeList 集合，每个元素都传入委托 F，委托返回泛型值。Each 函数返回所有泛型值的数组。
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="NodeList"></param>
        /// <param name="F"></param>
        /// <returns>函数返回所有委托返回的值数组。</returns>
        public static T[] Each<T>(this XmlNodeList NodeList, Func<XmlNode, T> F)
        {
            T[] TList = new T[NodeList.Count];
            int Counter = 0;
            foreach (XmlNode Item in NodeList)
            {
                TList[Counter++] = F(Item);
            }
            return TList;
        }

        public static XmlNode[] ToArray(this XmlNodeList NodeList)
        {
            int len = NodeList.Count;
            XmlNode[] Array = new XmlNode[len];
            for (int i = 0; i < len; i++)
            {
                Array[i] = NodeList[i];
            }
            return Array;
        }

        public static List<T> ToList<T>(this XmlNodeList NodeList, Func<XmlNode, T> F)
        {
            return NodeList.Select(n => F(n));
        }

        public static List<T> Select<T>(this XmlNodeList NodeList, Func<XmlNode, T> F)
        {
            return NodeList.Each(n => F(n)).ToList();
            //List<T> Arr = new List<T>();                
            //foreach(XmlNode Node in NodeList)
            //{
            //    Arr.Add(F(Node));
            //}
            //return Arr;
        }

        public static List<XmlNode> Where(this XmlNodeList NodeList, Func<XmlNode, bool> F)
        {
            List<XmlNode> List = new List<XmlNode>();
            NodeList.Each(n =>
            {
                if (F(n) == true)
                {
                    List.Add(n);
                }
            });
            return List;
        }

        public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(this XmlNodeList NodeList, Func<XmlNode, TKey> Key, Func<XmlNode, TValue> Value)
        {
            List<XmlNode> Arr = NodeList.Select(n => n);
            Dictionary<TKey, TValue> Dict = new Dictionary<TKey, TValue>();
            foreach (XmlNode item in Arr)
            {
                TKey key = Key(item);
                Dict[key] = Value(item);
            }
            return Dict;
        }
        public static List<XmlNode> OrderBy(this XmlNodeList NodeList, Func<XmlNode, XmlNode, int> Comparison)
        {
            List<XmlNode> Temp = NodeList.ToList(n => n);
            Comparison<XmlNode> cc = new Comparison<XmlNode>(Comparison);
            Temp.Sort(cc);
            return Temp;
        }

        /// <summary>
        /// NodeList 的中每个元素都执行 XPath 查询，XPath 表达式的轴必须从单个 Node 开始计算。仅可查询 Node 的属性与 InnerText。
        /// </summary>
        /// <param name="NodeList"></param>
        /// <param name="XPath"></param>
        /// <returns></returns>
        public static List<XmlNode> OrderBy(this XmlNodeList NodeList, string XPath)
        {
            return NodeList.OrderBy((m, n) =>
            {
                XmlNode x = m.SelectSingleNode(XPath);
                XmlNode y = n.SelectSingleNode(XPath);
                if (x.NodeType == XmlNodeType.Element)
                {
                    return string.Compare(x.InnerText, y.InnerText, true);
                }
                else
                {
                    return string.Compare(x.Value, y.Value, true);
                }
            });
        }

        public static int RemoveAll(this XmlNodeList NodeList)
        {
            int Len = NodeList.Count;
            for (int i = 0; i < Len; i++)
            {
                XmlNode Node = NodeList[i];
                Node.ParentNode.RemoveChild(Node);
            }
            return Len;
        }

        /// <summary>
        /// 删除指定的元素节点。内部使用递归方式，从顶部到底部遍历，凡是符合 XPath 查询的元素都将被删除。返回实际被删除的元素数量。
        /// </summary>
        /// <param name="Dom"></param>
        /// <param name="XPath"></param>
        /// <returns></returns>
        public static int RemoveElements(this XmlDocument Dom, string XPath)
        {
            List<string> ss = new List<string>();
            
            int Counter = 0;
            HashSet<XmlNode> Nodes = Dom.SelectNodes(XPath).ToList(n => n).ToHashSet();
            RemoveXmlElem(Dom, Nodes, ref Counter);
            return Counter;
        }
        private static void RemoveXmlElem(XmlNode Node, HashSet<XmlNode> Nodes, ref int Counter)
        {

            if (Node.NodeType != XmlNodeType.Document)
            {
                if (Node.NodeType != XmlNodeType.Element) return;
                if (Node.HasChildNodes == false) return;
            }
            XmlNodeList List = Node.ChildNodes;
            for (int i = 0; i < List.Count; i++)
            {
                if (List[i].NodeType != XmlNodeType.Element) continue;
                if (Nodes.Contains(List[i]))
                {
                    List[i].ParentNode.RemoveChild(List[i]);
                    Counter++;
                    i--; //每删除一个节点，List.Count 收缩1
                    continue;
                }
                RemoveXmlElem((XmlElement)List[i], Nodes, ref Counter);
            }
        }

        public static XmlNodeList SelectNodes(this XmlNode Node, string XPath, params string[] Args)
        {
            return Node.SelectNodes(string.Format(XPath, Args));
        }
        public static XmlNodeList SelectNodes(this XmlNode Node, XmlNamespaceManager nsmgr, string XPath, params string[] Args)
        {
            return Node.SelectNodes(string.Format(XPath, Args), nsmgr);
        }
        public static XmlNode SelectSingleNode(this XmlNode Node, string XPath, params string[] Args)
        {
            return Node.SelectSingleNode(string.Format(XPath, Args));
        }
        public static XmlNode SelectSingleNode(this XmlNode Node, XmlNamespaceManager nsmgr, string XPath, params string[] Args)
        {
            return Node.SelectSingleNode(string.Format(XPath, Args), nsmgr);
        }

        public static bool SetAttribute(this XmlNode Node, string Name, string Value)
        {
            if (Node.NodeType != XmlNodeType.Element) return false;
            if (Node.HasAttribute(Name) == false)
            {
                XmlAttribute Attr = Node.OwnerDocument.CreateAttribute(Name);
                Attr.Value = Value;
                Node.Attributes.Append(Attr);
            }
            else
            {
                Node.Attributes[Name].Value = Value;
            }
            return true;
        }

        public static string GetAttribute(this XmlNode Node, string Name)
        {
            if (Node.NodeType != XmlNodeType.Element) return null;
            return Node.HasAttribute(Name) == true ? Node.Attributes[Name].Value : null;
        }

        public static bool HasAttribute(this XmlNode Node, string Name)
        {
            if (Node.NodeType != XmlNodeType.Element) return false;
            return Node.Attributes[Name] != null;
        }
        /// <summary>
        /// 加载文件，默认采用 FileShare.ReadWrite 共享方式。在加载过程中，允许其他进程对目标文件进行 ReadWrite 操作。
        /// </summary>
        /// <param name="Dom">XmlDocument 对象</param>
        /// <param name="FileFullPath">目标文件路径</param>
        public static void LoadFile(this XmlDocument Dom, string FileFullPath)
        {
            LoadFile(Dom, FileFullPath, FileShare.ReadWrite);
        }
        /// <summary>
        /// 加载文件，默认采用 FileShare.ReadWrite 共享方式。在加载过程中，允许其他进程对目标文件进行 ReadWrite 操作。
        /// </summary>
        /// <param name="Dom">XmlDocument 对象</param>
        /// <param name="FileFullPath">目标文件路径</param>
        /// <param name="FileShare">文件共享方式。默认为 FileShare.ReadWrite</param>
        public static void LoadFile(this XmlDocument Dom, string FileFullPath, FileShare FileShare)
        {
            using (FileStream fs = new FileStream(FileFullPath, FileMode.Open, FileAccess.Read, FileShare))
            {
                Dom.Load(fs);
            }
        }
        /// <summary>
        /// 保存XmlDocument对象到文件，默认启用 FileShare.Read，保存过程中，其他进程仅可使用 Read 操作访问 FileFullPath 位置的文件。
        /// </summary>
        /// <param name="Dom">XmlDocument 对象</param>
        /// <param name="FileFullPath">文件存储的路径</param>
        public static void SaveToFile(this XmlDocument Dom, string FileFullPath)
        {
            SaveToFile(Dom, FileFullPath, FileShare.Read);
        }
        /// <summary>
        /// 保存XmlDocument对象到文件，默认启用 FileShare.Read，写入文件的过程中，其他进程仅可使用 Read 操作访问 FileFullPath 位置的文件。
        /// </summary>
        /// <param name="Dom">XmlDocument 对象</param>
        /// <param name="FileFullPath">文件存储的路径</param>
        /// <param name="FileShare">文件共享方式。默认为 FileShare.Read</param>
        public static void SaveToFile(this XmlDocument Dom, string FileFullPath, FileShare FileShare)
        {

            using (FileStream fs = new FileStream(FileFullPath, FileMode.Create, FileAccess.Write, FileShare))
            {
                Dom.Save(fs);
            }
        }

        public static XElement ToXElement(this XmlNode Node, LoadOptions Options)
        {
            using (XmlNodeReader reader = new XmlNodeReader(Node))
            {
                return XElement.Load(reader, Options);
            }
        }

        public static XElement ToXElement(this XmlNode Node)
        {
            using (XmlNodeReader reader = new XmlNodeReader(Node))
            {
                return XElement.Load(reader);
            }
        }

        public static XmlNode ToXmlNode(this XElement XElement)
        {
            using (XmlReader Reader = XElement.CreateReader())
            {
                XmlDocument dom = new XmlDocument();
                dom.Load(Reader);
                return dom.DocumentElement;
            }
        }

        public static XmlNode ToXmlNode(this XElement XElement, XmlDocument dom)
        {
            using (XmlReader Reader = XElement.CreateReader())
            {
                XmlNode node = dom.CreateElement("temparay");
                node.InnerXml = XElement.ToString();
                return node.FirstChild;
            }
        }

        public static XDocument ToXDocument(this XmlDocument Document, LoadOptions Options)
        {
            using (XmlNodeReader reader = new XmlNodeReader(Document))
            {
                return XDocument.Load(reader, Options);
            }
        }

        public static XDocument ToXDocument(this XmlDocument Document)
        {
            return Document.ToXDocument();
        }

        public static XmlDocument ToXmlDocument(this XDocument xdom)
        {
            using (XmlReader Reader = xdom.CreateReader())
            {
                XmlDocument dom = new XmlDocument();
                dom.Load(Reader);
                return dom;
            }
        }

        public static XPathDocument ToXPathDocument(this XDocument xdom)
        {
            return new XPathDocument(new StringReader(xdom.ToString()));
        }

        public static XPathNavigator ToXPathNavigator(this XDocument xdom)
        {
            XPathNavigator nav = xdom.CreateNavigator();
            nav.MoveToRoot();
            return nav;
        }

        public static XPathNavigator ToXPathNavigator(this XElement XElem)
        {
            XPathNavigator nav = XElem.CreateNavigator();
            nav.MoveToRoot();
            return nav;
        }

        public static XElement SelectSingleNode(this XDocument xdom, string XPathExpression)
        {
            return xdom.XPathSelectElement(XPathExpression);
        }

        /// <summary>
        /// 只能选择到 XElement，不能选择 XAttribute。
        /// </summary>
        /// <param name="xdom"></param>
        /// <param name="XPathExpression"></param>
        /// <returns></returns>
        public static XElement[] SelectNodes(this XDocument xdom, string XPathExpression)
        {
            return xdom.XPathSelectElements(XPathExpression).ToArray();
        }

        public static List<string> SelectAttributes(this XDocument xdom, string XPathExpressionAttribute)
        {
            List<string> List = new List<string>();
            foreach (object value in xdom.XPathEvaluate(XPathExpressionAttribute) as IEnumerable)
            {
                if (value is XAttribute)
                {
                    List.Add((value as XAttribute).Value);
                }
            }
            return List;
        }

        public static XAttribute SelectAttribute(this XDocument xdom, string XPathExpressionAttribute)
        {
            foreach (object value in xdom.XPathEvaluate(XPathExpressionAttribute) as IEnumerable)
            {
                if (value is XAttribute)
                {
                    return (value as XAttribute);
                }
            }
            return null;
        }

        public static XElement SelectSingleNode(this XElement XElement, string XPathExpression)
        {
            return XElement.XPathSelectElement(XPathExpression);
        }

        public static XElement[] SelectNodes(this XElement XElement, string XPathExpression)
        {
            return XElement.XPathSelectElements(XPathExpression).ToArray();
        }

        /// <summary>
        /// 获取 XElement 元素的指定属性值，如果属性不存在，返回 null
        /// </summary>
        /// <param name="XElement"></param>
        /// <param name="Name"></param>
        /// <returns></returns>
        public static string GetAttribute(this XElement XElement, string Name)
        {
            XName xn = XElement.GetDefaultNamespace().GetName(Name);
            if (XElement.Attribute(xn) == null) return null;
            return XElement.Attribute(xn).Value;
        }
        /// <summary>
        /// 与 GetAttribute 方法类似。唯一区别是当属性不存在时，返回 string.Empty
        /// </summary>
        /// <param name="XElem"></param>
        /// <param name="Name"></param>
        /// <returns></returns>
        public static string GetAttrValue(this XElement XElem, string Name)
        {
            string Val = GetAttribute(XElem, Name);
            return Val == null ? "" : Val;
        }

        public static void SetAttribute(this XElement XElement, string Name, string Value)
        {
            if (Value == null)
            {
                Value = "";
            }
            if (XElement.Attribute(Name) == null)
            {
                XElement.Add(new XAttribute(Name, Value));
            }
            else
            {
                XElement.Attribute(Name).Value = Value;
            }
        }


    }

   
    
}
